package base;

import static util.Constant.STR_INVALID_ID;
import static util.Constant.STR_ITEM_LIST_ALREADY_EXIST;
import static util.Constant.STR_ITEM_LIST_NOT_EXIST;
import static util.Constant.STR_USER_ALREADY_FRIEND;
import static util.Constant.STR_USER_NOT_FRIEND;
import static util.Tool.Sha1;

import java.util.HashSet;

/**
 * The User {@code class}. Create User object with an ID, a login, a password, a
 * list of Itemlist and a list of friends.
 * 
 * @author Peter NGO, Guillaume POUSSEL and Mathieu RUL
 * 
 * @version 1.0
 */
public class User {
	
	/**
	 * ID of the User.
	 */
	private Integer ID;
	
	/**
	 * Login of the User.
	 */
	private String login;
	
	/**
	 * Password of the User.
	 */
	private String password;
	
	/**
	 * Status of the User, connected or not.
	 */
	private Boolean connected = false;
	
	/**
	 * Last name of the User.
	 */
	private String lastname;
	
	/**
	 * First name of the User.
	 */
	private String firstname;
	
	/**
	 * List of itemList of the User.
	 */
	private HashSet<Integer> itemLists;
	
	/**
	 * List of friend of the User.
	 */
	private HashSet<Integer> friends;

	/**
	 * Constructor of the {@code class}. Create an User.
	 */
	public User() {
		this.ID = null;
		this.login = "";
		this.password = "";
		this.firstname = "";
		this.lastname = "";
		this.friends = new HashSet<Integer>();
		this.itemLists = new HashSet<Integer>();
	}

	/**
	 * Constructor of the {@code class}. Create an User from an ID.
	 * 
	 * @param ID
	 *            the user ID.
	 * 
	 * @throws IllegalArgumentException
	 */
	public User(Integer ID) throws IllegalArgumentException {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);

		this.ID = ID;
	}

	/**
	 * Constructor of the {@code class}. Create an User from parameter.
	 * 
	 * @param ID
	 *            the user ID.
	 * 
	 * @param login
	 *            the user login.
	 * 
	 * @param password
	 *            the user password.
	 * 
	 * @param firstname
	 *            the user firstname.
	 * 
	 * @param lastname
	 *            the user lastname.
	 * 
	 * @param friends
	 *            the user friends list.
	 * 
	 * @param itemLists
	 *            the user itemLists list.
	 * 
	 * @throws IllegalArgumentException
	 */
	public User(Integer ID, String login, String password, String firstname,
			String lastname, HashSet<Integer> friends,
			HashSet<Integer> itemLists) throws IllegalArgumentException {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);

		this.ID = ID;
		this.login = login;
		this.password = password;
		this.firstname = firstname;
		this.lastname = lastname;
		setFriends(friends);
		setItemLists(itemLists);
	}

	/**
	 * Add a friend to current user.
	 * 
	 * @param ID
	 *            the friend's user ID.
	 * 
	 * @throws Exception
	 */
	public void addFriend(Integer ID) throws Exception {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);
		if (!friends.add(ID))
			throw new Exception(STR_USER_ALREADY_FRIEND);
	}

	/**
	 * Add an ItemList to User.
	 * 
	 * @param ID
	 *            the ItemList's ID.
	 * 
	 * @throws Exception
	 */
	public void addItemList(Integer ID) throws Exception {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);
		if (!itemLists.add(ID))
			throw new Exception(STR_ITEM_LIST_ALREADY_EXIST);
	}

	/**
	 * Connect an user.
	 * 
	 * @param password
	 *            the user password.
	 * 
	 * @return {@code true} if user is connected; {@code false} otherwise.
	 */
	public Boolean connect(String password) {
		connected = this.password.equals(Sha1(password));
		return connected;
	}

	/**
	 * Disconnect an User.
	 */
	public void disconnect() {
		connected = false;
	}

	/**
	 * Check equality between two users.
	 * 
	 * If this method get an User, it compares the two IDs, else it returns
	 * false.
	 * 
	 * @param object
	 *            Another object to compare.
	 * 
	 * @return {@code true} if the parameter are an User; {@code false}
	 *         otherwise.
	 */
	public boolean equals(Object object) {
		return ((object instanceof User) && ((User) object).getID() == this.ID);
	}
	
	/**
	 * Get the User's first name.
	 * 
	 * @return the firstname.
	 */
	public String getFirstname() {
		return firstname;
	}
	

	/**
	 * Get the User's friends list.
	 * 
	 * @return the friends.
	 */
	public HashSet<Integer> getFriends() {
		return friends;
	}

	/**
	 * Get the User's ID.
	 * 
	 * @return the ID.
	 */
	public Integer getID() {
		return ID;
	}

	/**
	 * Get the User's itemLists list.
	 * 
	 * @return the itemLists.
	 */
	public HashSet<Integer> getItemLists() {
		return itemLists;
	}

	/**
	 * Get the User's last name.
	 * 
	 * @return the lastname.
	 */
	public String getLastname() {
		return lastname;
	}

	/**
	 * Get the User's login.
	 * 
	 * @return the login.
	 */
	public String getLogin() {
		return login;
	}

	/**
	 * Get the User's password.
	 * 
	 * @return the password.
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Get an hash representation of the User.
	 * 
	 * @return the hash representation of the User.
	 */
	@Override
	public int hashCode() {
		return null != ID ? ID : 0;
	}

	/**
	 * Verify if an user is connected.
	 * 
	 * @return {@code true} if user is connected; {@code false} otherwise.
	 */
	public Boolean isConnected() {
		return connected;
	}

	/**
	 * Test if the user ID is friend.
	 * 
	 * @param ID
	 *            the user ID to test.
	 * 
	 * @return {@code true} if user is friend; {@code false} otherwise.
	 */
	public Boolean isFriend(Integer ID) {
		return friends.contains(ID);
	}

	/**
	 * Test if two user are friends.
	 * 
	 * @param user1
	 *            the first user to test.
	 * 
	 * @param user2
	 *            the second user to test.
	 * 
	 * @throws Exception
	 */
	public Boolean isFriends(User user1, User user2) throws Exception {
		return user1.isFriend(user2.getID()) && user2.isFriend(user1.getID());
	}

	/**
	 * Returns a {@code String} object representing the User's profile value.
	 * 
	 * @return a string representation of the User.
	 */
	public String profile() {
		return "User ID: " + ID + "\nLogin: " + login + "\nAKA: " + firstname
				+ " " + lastname + "\n";
	}

	/**
	 * Remove a friend from current user.
	 * 
	 * @param ID
	 *            the friend's user ID.
	 * 
	 * @throws Exception
	 */
	public void removeFriend(Integer ID) throws Exception {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);
		if (!friends.remove(ID))
			throw new Exception(STR_USER_NOT_FRIEND);
	}

	/**
	 * Remove an ItemList from current user.
	 * 
	 * @param ID
	 *            the ItemList's ID.
	 * 
	 * @throws Exception
	 */
	public boolean removeItemList(Integer ID) throws Exception {
		if (ID < 0)
			throw new IllegalArgumentException(STR_INVALID_ID);
		if (!itemLists.remove(ID))
			throw new Exception(STR_ITEM_LIST_NOT_EXIST);
		return true;
	}

	/**
	 * Set the User's firstname.
	 * 
	 * @param firstname
	 *            The first name to set.
	 */
	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	/**
	 * Set the User's friends list.
	 * 
	 * @param friends
	 *            the friends list to set.
	 */
	public void setFriends(HashSet<Integer> friends) {
		this.friends = friends;
	}

	/**
	 * Set the User's itemLists list.
	 * 
	 * @param itemLists
	 *            the itemLists list to set.
	 */
	public void setItemLists(HashSet<Integer> itemLists) {
		this.itemLists = itemLists;
	}

	/**
	 * Set the User's lastname.
	 * 
	 * @param lastname
	 *            the last name to set.
	 */
	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	/**
	 * Set the User's login.
	 * 
	 * @param login
	 *            the login to set.
	 */
	public void setLogin(String login) {
		this.login = login;
	}

	/**
	 * Set the User's password.
	 * 
	 * @param password
	 *            the password to set.
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * Returns a {@code String} object representing the User's value.
	 * 
	 * @return a string representation of the User.
	 */
	@Override
	public String toString() {
		return "User ID: " + ID + "\nLogin: " + login + "\nFirstname: "
				+ firstname + "\nLastname: " + lastname + "\nFriends: "
				+ friends + "\nItemLists: " + itemLists + "\n";
	}
}
